/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */

package cn.hermit.nio.http.server;

import cn.hermit.nio.http.Http;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

/**
 * @author JackGao 高建国
 * @description //TODO 设计说明
 * @date 29/5/2017
 */
public class HttpRequestParser implements Http {

    private static CharsetDecoder decoder = Charset.forName("ISO-8859-1").newDecoder();
    private static final byte[] HEAD_END = new byte[]{13, 10, 13, 10};
    private static final byte[] HEAD = new byte[]{72, 69, 65, 68};
    private static final byte[] GET = new byte[]{71, 69, 84, 32};
    private static final byte[] POST = new byte[]{80, 79, 83, 84};
    private static final byte[] PUT = new byte[]{80, 85, 84};
    private static final byte[] DELETE = new byte[]{68, 69, 76, 69, 84, 69};
    private static final byte[] OPTIONS = new byte[]{79, 80, 84, 73, 79, 78, 83};

    /**
     * @param data   to search from
     * @param tofind target
     * @param start  start index
     * @return index of the first find if find, data.length if not find
     */
    public static int findSub(byte[] data, byte[] tofind, int start) {
        int index = data.length;
        outer:
        for (int i = start; i < data.length; ++i) {

            for (int j = 0; j < tofind.length; ) {
                if (data[i] == tofind[j]) {
                    ++i;
                    ++j;
                    if (j == tofind.length) {
                        index = i - tofind.length;
                        break outer;
                    }
                } else {
                    i = i - j; // step back
                    break;
                }
            }
        }
        return index;
    }

    // private static Logger logger = Logger.getLogger(HttpRequestHeader.class);

    /**
     * same as {@link #findSub(byte[], byte[], int)},except find from end to
     * start;
     *
     * @param data   to search from
     * @param tofind target
     * @param start  start index
     * @return index of the first find if find, data.length if not find
     */
    public static int findSubFromEnd(byte[] data, byte[] tofind, int start) {
        int index = data.length;
        outer:
        for (int i = data.length - tofind.length; i > 0; --i) {

            for (int j = 0; j < tofind.length; ) {
                if (data[i] == tofind[j]) {
                    ++i;
                    ++j;
                    if (j == tofind.length) {
                        index = i - tofind.length;
                        break outer;
                    }
                } else {
                    i = i - j; // step back
                    break;
                }
            }
        }
        return index;
    }

    public static void main(String[] args) throws CharacterCodingException {

        CharBuffer s = decoder.decode(ByteBuffer.wrap(new byte[]{72, 69, 65, 68}));

        System.out.println(s);

        byte[] data = {12, 13, 10, 13, 13, 10, 13, 10, 13, 17};

        int i = findSubFromEnd(data, HEAD_END, 0);
        data = new byte[]{12, 13, 10, 13, 13, 10, 13, 10, 10, 10};
        i = 0;
        while (i != -1) {
            i = findSub(data, new byte[]{13, 10}, i);
            System.out.println(i);
        }

    }

    // private Version version;
    private boolean begin = false;

    private CharBuffer charBuffer = ByteBuffer.allocate(4096).asCharBuffer();

    private HttpRequest request;

    public boolean appendSegment(byte[] segment) {

        int beginIndex = 0;

        if (begin == false) {
            request = new HttpRequest();
            if ((beginIndex = findSub(segment, HEAD, 0)) != segment.length) {
                request.setVerb(Verb.HEAD);
            } else if ((beginIndex = findSub(segment, GET, 0)) != segment.length) {
                request.setVerb(Verb.GET);
            } else if ((beginIndex = findSub(segment, POST, 0)) != segment.length) {
                request.setVerb(Verb.POST);
            } else if ((beginIndex = findSub(segment, PUT, 0)) != segment.length) {
                request.setVerb(Verb.PUT);
            } else if ((beginIndex = findSub(segment, DELETE, 0)) != segment.length) {
                request.setVerb(Verb.DELETE);
            } else if ((beginIndex = findSub(segment, OPTIONS, 0)) != segment.length) {
                request.setVerb(Verb.OPTIONS);
            } else {
                // not begin yet, and find no begin, just return false;
                return false;

            }
            begin = true;

        }

        int endIndex = findSubFromEnd(segment, HEAD_END, 0);
        ByteBuffer b = ByteBuffer.wrap(segment, beginIndex, endIndex);
        decoder.decode(b, charBuffer, endIndex != segment.length);
        if (endIndex != segment.length) {
            extractValueAndReset();
            int leftByteCount = segment.length - (endIndex + HEAD_END.length);
            if (leftByteCount > 0) {
                byte[] body = new byte[leftByteCount];
                System.arraycopy(segment, endIndex + HEAD_END.length, body, 0, leftByteCount);
                request.setBody(body);
            }
            return true;
        }
        return false;
    }

    private void extractValueAndReset() {
        charBuffer.flip();
        String head = charBuffer.toString();
        String[] lines = head.split("\r\n");
        String[] split = lines[0].split(" ");

        request.setRequestURI(split[1]);
        String version = split[2];
        if (version.contains("/1.1")) {
            request.setVersion(Version.HTTP11);
        } else {
            request.setVersion(Version.HTTP10);
        }
        String[] temp = null;
        for (int i = 1; i < lines.length; ++i) {
            String line = lines[i];
            int index = line.indexOf(':');
            if(index != -1) {
                request.getHeaderMap().put(line.substring(0, index), line.substring(index+1));
            }
        }

        charBuffer.clear();
        decoder.reset();
        begin = false;
    }

    public HttpRequest getHttpRequest() {
        return request;
    }

}
